﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Psm.PubSub.Messaging;
using System.Threading;
using Psm.Util;

namespace Psm.PubSub.StateCache
{
    internal class DataMessageDecorator
    {
        private SpinLock dataLock;
        private IList<QueryMessageDecorator> _registry;
        public DataMessageInternal DataMessage { get; private set; }

        public DataMessageDecorator(DataMessageInternal message)
        {
            this.DataMessage = message;
        }

        public DataMessageDecorator()
        {
            dataLock = new SpinLock();
            _registry = new List<QueryMessageDecorator>();
        }

        internal void SetMessage(DataMessageInternal message)
        {
            this.DataMessage = message;
            dataLock = new SpinLock();
            _registry = new List<QueryMessageDecorator>();
        }

        public void MergeMessage(DataMessageInternal other)
        {
            bool lockTaken = false;
            try
            {
                dataLock.Enter(ref lockTaken);
                this.DataMessage.MergeMessage(other);
                for(int index=0; index < _registry.Count; index++)
                {
                    //TODO:check query still valid ???
                    _registry[index].PublishMessage(other);
                }
            }
            finally
            {
                if( lockTaken ) 
                    dataLock.Exit(true);
            }
        }

        public bool EvaluateQuery(QueryMessageDecorator query)
        {
            bool added = false;
            bool lockTaken = false;
            try
            {
                dataLock.Enter(ref lockTaken);
                bool registerQuery = true;

                foreach(string key in query.QueryMessage.Filters.Keys)
                {
                    string value = query.QueryMessage.Filters[key];
                    SchemaField field = this.DataMessage.Schema.GetField(key);
                    if (field == null)
                    {
                        registerQuery = false;
                        break;
                    }                    
                    if (!ByteBuffer.Equals(this.DataMessage.GetData(field), value))
                    {
                        registerQuery = false;
                        break;
                    }
                }

                if (registerQuery)
                {
                    _registry.Add(query);
                    query.PublishMessage(this.DataMessage);
                }
            }
            finally
            {
                if (lockTaken) 
                    dataLock.Exit(true);
            }
            return added;
        }

        public void RemoveQuery(QueryMessageDecorator query)
        {   
            bool lockTaken = false;
            try
            {
                dataLock.Enter(ref lockTaken);
                _registry.Remove(query);
            }
            finally
            {
                if (lockTaken)
                    dataLock.Exit(true);
            }
        }
    }
}
